package com.rockwell.robot2015.model.states;

import android.os.Looper;
import android.util.Log;

import com.rockwell.robot2015.model.IRobotLogger;
import com.rockwell.robot2015.model.Robot;
import com.rockwell.robot2015.model.states.running.RobotRunningState;
import com.rockwell.robot2015.model.states.running.RobotRunningStateMachine;

public class RobotStateMachine implements IStateMachine {

	Robot m_robot;
	
	boolean m_start = false;
	
	RobotState m_currentState = RobotState.Initial;
	RobotState m_nextState = null;
	
	RobotRunningStateMachine m_runningMachine;
	
	private Object m_pauseResumeLock = new Object();

	public RobotStateMachine(Robot robot) {
		m_robot = robot;
		
		m_runningMachine = new RobotRunningStateMachine(this);
		
		RobotState.setRunningMachine(m_runningMachine);
	}
	
	/**
	 * Always be called by the worker thread
	 */
	@Override
	public void move() {
		if (Looper.myLooper() == Looper.getMainLooper()) {
			throw new RuntimeException("RobotStateMachine.move() cannot be called by the main thread");
		}
		
		if (m_currentState.equals(RobotState.Initial)) {
			m_currentState.enter(this);
			m_currentState.move(this);
			return;
		}
		
		if (m_start) {
			if (m_currentState.equals(RobotState.Initialized)) {
				m_nextState = RobotState.Running;
				m_start = false;
				
				m_robot.log("Set next state to Running", 2);
			}
			else {
				m_nextState = RobotState.Initial;
				return;
			}
		}
		
		if (m_nextState != null) {
			changeState(m_nextState);
			m_nextState = null;
		}
		
		if (m_currentState.equals(RobotState.Stopped)) {
			m_robot.sleep(200);
		}
		else {
			m_currentState.move(this);
			
			if (m_robot.isInStepMode()) {
				m_runningMachine.beforePause();
				waitForResume();
				m_runningMachine.afterResume();
			}
		}
	}

	public void setNextState(RobotState state) {
		m_nextState = state;
	}
	
	/**
	 * Should always be called by the worker thread
	 * @throws Exception 
	 */
	@Override
	public void changeState(IState state)  {
		if (Looper.myLooper() == Looper.getMainLooper()) {
			throw new RuntimeException("RobotStateMachine.changeState cannot be called by the main thread");
		}
		
		if (m_currentState != null) {
			m_currentState.exit(this);
		}
		
		m_currentState = (RobotState)state;
		m_currentState.enter(this);

		m_robot.setCurrentState(state.toString());
	}

	@Override
	public IState getState() {
		return m_currentState;
	}

	@Override 
	public void reset() {
		m_runningMachine.reset();
		m_nextState = RobotState.Initial;
	}
	
	@Override
	public Robot robot() {
		return m_robot;
	}
	
	public RobotRunningStateMachine runningStateMachine() {
		return m_runningMachine;
	}
	
	public void setNewRunningState(RobotRunningState newState) {
		m_runningMachine.setNewState(newState);
	}
	
	/**
	 * Start the robot.
	 * Called by the UI thread which shouldn't call changeState()
	 */
	public void start() {
		synchronized (m_pauseResumeLock) {
			m_currentState = RobotState.Initial;
			m_start = true;
			
			// Wake up the worker thread 
			m_pauseResumeLock.notify();
		}
	}
	
	/**
	 * Stop the robot.
	 * Called by the UI thread which shouldn't call changeState()
	 */
	public void stop() {
		m_nextState = RobotState.Stopped;
	}
	
	/**
	 * Pause the robot
	 * Called by the UI thread which shouldn't call changeState()
	 */
	public void pause() {
		synchronized (m_pauseResumeLock) {
			m_nextState = RobotState.Paused;
		}
	}
	
	/**
	 * Resume the robot
	 * Called by the UI thread which shouldn't call changeState()
	 */
	public void resume() {
		synchronized (m_pauseResumeLock) {
			m_nextState = RobotState.Running;
			
			// Wake up the worker thread
			m_pauseResumeLock.notify();
		}
	}
	
	public boolean isPaused() {
		return m_currentState.equals(RobotState.Paused);
	}
	
	@Override
	public void waitForResume() {
		synchronized (m_pauseResumeLock) {
			try {
				if (m_currentState == RobotState.Initial)
					return;

				m_currentState = RobotState.Paused;
				
				m_pauseResumeLock.wait();
				
				if (m_currentState != RobotState.Initial) {
					m_currentState = RobotState.Running;

					m_robot.onRobotResumed();
				}
				else {
					m_robot.onRobotIdle();
				}
			}
			catch(InterruptedException e) { 
				//Log.i(TAG, "resume error"); 
			} 
		}
	}
	
	private void runBeforeStart() {
		/*
		sendCommandSequence = SendCommandEnum.SEND;
		
		if (m_needSendJogCommandBeforeStart) {
			while (performSendCommand(RobotAction.Jog, m_idealOrientation) != 1) {
				if (sendCommandSequence == SendCommandEnum.ERROR) {
					sendCommandSequence = SendCommandEnum.SEND;
					break;
				}
			};
			
			m_needSendJogCommandBeforeStart = false;
		}
		*/
	}
	
	private void runBeforePause() {
		/*
		sendCommandSequence = SendCommandEnum.SEND;
		
		if (m_commander.isJogging()) {
			while (performSendCommand(RobotAction.Stop) != 1) {
				if (sendCommandSequence == SendCommandEnum.ERROR) {
					sendCommandSequence = SendCommandEnum.SEND;
					break;
				}
			};
			m_needSendJogCommandBeforeStart = true;
		}
		*/
	}


}
